package xmath

import (
	"fmt"
	"math"
	"soluty.cc/go/xlog"
)

// int32数字的四则运算，溢出时会报错

// 乘法 x * percent / power
func Multi(x int32, percent int32, power ...int32) (int32, error) {
	var r = int64(x)
	nth := int32(1)
	if len(power) > 0 {
		nth = power[0]
	}
	if nth <= 0 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("power=%v错误,必须是正整数", power))
	}
	r = r * int64(percent) / int64(nth)
	if r < math.MinInt32 || r > math.MaxInt32 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("乘法溢出: %v*%v/%v = %v", x, percent, nth, r))
	}
	return int32(r), nil
}

// 乘法 向上取整
func MultiCeil(x int32, percent int32, power ...int32) (int32, error) {
	var r = int64(x)
	nth := int32(1)
	if len(power) > 0 {
		nth = power[0]
	}
	if nth <= 0 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("power=%v错误,必须是正整数", power))
	}
	r = (r*int64(percent) + int64(nth) - 1) / int64(nth)
	if r < math.MinInt32 || r > math.MaxInt32 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("乘法溢出: %v*%v/%v = %v", x, percent, nth, r))
	}
	return int32(r), nil
}

// 除法
func Divide(x int32, y int32) (int32, error) {
	if y == 0 {
		return 0, fmt.Errorf("除零错误: %v/%v", x, y)
	}
	r := int64(x) / int64(y)
	if r < math.MinInt32 || r > math.MaxInt32 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("除法溢出: %v/%v = %v", x, y, r))
	}
	return int32(r), nil
}

// 除法， 向上取整
func DivideCeil(x int32, y int32) (int32, error) {
	if y == 0 {
		return 0, fmt.Errorf("除零错误: %v/%v", x, y)
	}
	r := (int64(x) + int64(y) - 1) / int64(y)
	if r < math.MinInt32 || r > math.MaxInt32 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("除法溢出: %v/%v = %v", x, y, r))
	}
	return int32(r), nil
}

// 百分比
func MultiPercent(x int32, p int32) (int32, error) {
	return Multi(x, p, 100)
}

// 百分比 向上取整
func MultiPercentCeil(x int32, p int32) (int32, error) {
	return MultiCeil(x, p, 100)
}

// 万分比
func MultiPerMyriad(x int32, p int32) (int32, error) {
	return Multi(x, p, 10000)
}

// 万分比向上取整
func MultiPerMyriadCeil(x int32, p int32) (int32, error) {
	return MultiCeil(x, p, 10000)
}

// 加法
func Add(x, y int32) (int32, error) {
	v := x + y
	if (x^v)&(y^v) < 0 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("加法溢出: %v+%v = %v", x, y, v))
	}
	return v, nil
}

// 减法
func Sub(x, y int32) (int32, error) {
	r := int64(x) - int64(y)
	if r < math.MinInt32 || r > math.MaxInt32 {
		return 0, xlog.Errorf(xlog.Fmt(), "", fmt.Errorf("减法溢出: %v-%v = %v", x, y, r))
	}
	return int32(r), nil
}
